var last_rule_id = 1;

var lex_rule = function(str, f) {
	var out = [];
	var i, max = str.length;
	for (i = 0; i < max; ++i)
		out[i] = {};
	var action = function() { f(str) };
	action.toString = function() { return '[' + str + ']' };
	out[out.length] = { action: action };
	for (i = 0, max = out.length - 1; i < max; ++i)
		out[i][str[i]] = out[i + 1];
	return out[0];
};


var lex_merge = function(r1, r2) {
	r2 = objoin({}, r2);
	if (r1.default_action) {
		for (var i in r2) {
			if (i.length > 1) continue;
			r2[i].action = r1.default_action;
		}
		delete r1.default_action;
	}
	//if (!r1.any && r2.any) {
	//	r1.any = r2.any;
	//}
	
	if (r2.action && !r1.action) {
		r1.default_action = r2.action;
	}
	
	if (r1.description && r2.description)
		r1.description += ' | ' + r2.description;
	else if (!r1.description && r2.description)
		r1.description = r2.description;
	
	if (r2.next)
		r1.next = r2.next;
	
	
	for (var i in r2) {
		if (i.length > 1 && i != 'any') continue;
		if (!r1[i]) {
			//r1[i] = objoin({}, r2[i]);
			r1[i] = r2[i];
			continue;
		}
		lex_merge(r1[i], r2[i]);
	}

	if (r2.default_action) {
		x = {};
		if (r1.default_action)
			x.d = r1.default_action;
		y = { d: r2.default_action };
		obcat(x, y);
		r1.default_action = x.d;
	}
	
	return r1;
};


var lex_run = function(initial, input) {
	var state = initial;
	var c;
	//console.dir(state);
	var i = 0, max = input.length;
	yytext = '';
	var d;
	while (true) {
		if (i >= max) break;
				
		d = state.description ? state.description : 'Без описания';
		trace(d);
		
		if (state === initial)
			yytext = '';
		
		c = input[i];
		yytext += c;
		if (state[c]) {
			state = state[c];
			
		} else if (state.any) {
			state = state.any;
		} else {
			trace('&nbsp;&nbsp;"<b>%s</b>" &#8594;&nbsp;&nbsp;STOP', c);
			return;
		
			if (state.default_action)
				state.default_action();
			state = initial[c] || initial.any;
			yytext = c;
			if (!state) {
				state = initial;
				yytext = '';			
			}
		}
		++i;

		trace('&nbsp;&nbsp;"<b>' + c + '</b>" &#8594;&nbsp;&nbsp;' + (state.description ? state.description : 'Без описания'));
		trace('&nbsp;');

		if (state.action) {
			//trace('Светрка: <b>%s</b>', yytext);
			state.action();
			yytext = '';
			state = initial;
		}
		
	}
	if (state.default_action)
		state.default_action();
};


var lex_normalize = function(r) {
	var initial = r;
	var buf = [r];
	var i = 0, j, max, x;
	buf[0].locked = true;
	do {
		for (max = buf.length; i < max; ++i) {
			x = buf[i];
			x.locked = true;
			for (j in x) {
				if (j.length != 1 && j != 'any') continue;
				if (x[j].locked) continue;
				if (!is_object(x[j])) continue;
				buf[buf.length] = x[j];
			}
		}
	} while (max < buf.length);
	for (i = 0, max = buf.length; i < max; ++i) {
		delete buf[i].locked;
	}
	buf.shift();
	//console.dir(buf);
	//debugger;

	for (i = 0, max = buf.length; i < max; ++i) {
		lex_merge(buf[i], initial);
		//buf[i].normalized = true;
	}

	
	
	for (i = 0, max = buf.length; i < max; ++i) {
		if (!buf[i].default_action) continue;
		for (j in buf[i]) {
			if (j.length != 1) continue;
			if (!buf[i][j].action)
				buf[i][j].default_action = buf[i].default_action;
		}
		delete buf[i].default_action;
	}
	
	
	return buf;
};






var lex_run2 = function(initial, input) {
	var state = initial;
	var c;
	//console.dir(state);
	var i = 0, max = input.length;
	yytext = '';
	var d;
	var starts = {};
	while (true) {
		if (i >= max) break;
		if (state.start) {
			if (!starts[state.start])
				starts[state.start] = i;
		}
		if (state.cancel) {
			delete starts[state.cancel];
		}
				
		d = state.description ? state.description : 'Без описания';
		trace(d);
		
		if (state === initial)
			yytext = '';
		
		c = input[i];
		yytext += c;
		if (state[c]) {
			state = state[c];
			
		} else if (state.any) {
			state = state.any;
		} else {
			trace('&nbsp;&nbsp;"<b>%s</b>" &#8594;&nbsp;&nbsp;STOP', c);
			return;
		
			if (state.default_action)
				state.default_action();
			state = initial[c] || initial.any;
			yytext = c;
			if (!state) {
				state = initial;
				yytext = '';			
			}
		}
		++i;

		trace('&nbsp;&nbsp;"<b>' + c + '</b>" &#8594;&nbsp;&nbsp;' + (state.description ? state.description : 'Без описания'));
		trace('&nbsp;');

		if (state.hasOwnProperty('finish')) {
			var p = starts[state.finish];
			if (p) {
				yytext = input.substr(p, i - p + 1);
				delete starts[state.finish];
			}
			alert('finish: ' + state.finish + ': ' + yytext);
		}

		if (state.action) {
			//trace('Светрка: <b>%s</b>', yytext);
			state.action();
			yytext = '';
			state = initial;
		}
		
	}
	if (state.default_action)
		state.default_action();
};




var lex_run3 = function(initial, input) {
	var state = initial;
	var pos = 0;
	var max = input.length;
	var c;

	while (true) {
		c = input[pos];
		while (state = state[c]) {
			c = input[++pos];
			
		}
	}	
};










